<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>输出的五线谱</title>
<link href="note.svg" rel="shortcut icon" type="image/x-icon">
</head>
<body>
    <div class="item">
        <label for="File">上传文件</label>
        <input type="file" id="File" onchange="readfile(this.files[0])">
    </div>
    <div id="output"></div>
</body>
</html>
<script src="https://cdn.jsdelivr.net/npm/vexflow@4.0.3/build/cjs/vexflow.js"></script>
<script type="module">
    const restlocat=[
        "d/5","b/4","b/4","b/4","b/4","b/4",
        "f/3","d/3","d/3","d/3","d/3","d/3"
    ]
    const Duration=[
        "w","h","q","8","16","32"
    ]
    const { Renderer, Stave, Accidental, StaveNote, Beam, Formatter, Dot,StaveConnector,Voice } = Vex.Flow;
    window.readfile = (file) => {
        var reader = new FileReader()
        reader.readAsText(file);
        reader.onload = (res) => {
            var content=res.target.result;
            var array=content.split("\r\n")
            //console.log(array)
            var num=parseInt(array[0].split(' ')[0]);//小节数
            const rows=3;//列数
            const width=400;//一个小节的宽度
            const distance=400;//行距
            var stavenum=num;
            if((stavenum%rows)!=0)
                stavenum=stavenum+rows-(stavenum%rows)
            var beat=(array[0].split(' ')[1]);//节奏
            var frontbeat,backbeat;
            frontbeat=parseInt(beat.split('/')[0]);
            backbeat=parseInt(beat.split('/')[1]);
            //console.log(beat);
            
            // Create an SVG renderer and attach it to the DIV element named "boo".
            const div = document.getElementById("output");
            div.innerHTML="";
            const renderer = new Renderer(div, Renderer.Backends.SVG);
            // Configure the rendering context.
            renderer.resize(rows*width*1.2,stavenum*distance/rows);
            const context = renderer.getContext();
            const highstaves = new Array();
            const lowstaves = new Array();
            for(var i=0;i<stavenum;i++){//画空谱
                highstaves[i]=new Stave(120+(i%rows)*width,20+parseInt(i/rows)*distance,width);
                lowstaves[i]=new Stave(120+(i%rows)*width,190+parseInt(i/rows)*distance,width);
                const connector=new StaveConnector(highstaves[i],lowstaves[i]);
                if(i%rows==0){
                    highstaves[i].setClef("treble").addTimeSignature(beat);
                    lowstaves[i].setClef("bass").addTimeSignature(beat);
                    connector.setContext(context).setType('brace').draw();
                    connector.setType('singleLeft').draw();
                }
                connector.setContext(context).setType('singleRight').draw();
                highstaves[i].setContext(context).draw();
                lowstaves[i].setContext(context).draw();
            }
            var highend,lowend;
            for(var i=0;i<num;i++){//画高音谱
                console.log(array[i+2])
                var regex=new RegExp("low");
                if(regex.test(array[i+2])){
                    highend=i+3;
                    break;
                }
                var voicestrs=array[i+2].split('|');
                var voices=new Array();
                console.log(voicestrs);
                for(let j in voicestrs){
                    var notestrs=voicestrs[j].split(' ');
                    console.log(notestrs);
                    var notes=new Array();
                    var durations=new Array();
                    var sum=0;
                    for(let x in notestrs){
                        if(notestrs[x]!=''){
                            notes[notes.length]=cnote(notestrs[x],"treble");
                            durations[durations.length]=caldur(notestrs[x]);
                            console.log(x," dur:",durations[durations.length-1])
                            sum=sum+durations[durations.length-1];
                        }
                    }
                    console.log(sum);
                    addrest(notes,sum,"treble",durations);
                    // var beams = Beam.generateBeams(notes);
                    // beams.forEach((b) => {
                    //     b.setContext(context).draw();
                    // });
                    var voice = new Voice({ num_beats: frontbeat, beat_value: backbeat });
                    console.log(notes);
                    voice.addTickables(notes);
                    voices=voices.concat([voice]);
                }
                console.log("voices:",voices)
                // Format and justify the notes to 400 pixels.
                new Formatter().joinVoices(voices).format(voices, width);
                // Render voice
                voices.forEach(function (v) {
                    v.draw(context, highstaves[i]);
                });
            }
            for(var i=highend-3;i<stavenum;i++){  //补齐休止
                var notesh=[
                    new StaveNote({keys: ["d/5"],duration:"wr",clef:"treble"})
                ]
                Formatter.FormatAndDraw(context, highstaves[i], notesh);
            }
            for(var i=highend;i<num+highend;i++){//画低音谱
                var regex=new RegExp("end");
                if(regex.test(array[i])){
                    lowend=i-highend;
                    break;
                }
                console.log(array[i]);
                var voicestrs=array[i].split('|');
                var voices=new Array();
                
                for(let j in voicestrs){
                    var notestrs=voicestrs[j].split(' ');
                    console.log(notestrs);
                    var notes=new Array();
                    var durations=new Array();
                    var sum=0;
                    for(let x in notestrs){
                        if(notestrs[x]!=''){
                            notes[notes.length]=cnote(notestrs[x],"bass");
                            durations[durations.length]=caldur(notestrs[x]);
                            console.log(x," dur:",durations[durations.length-1])
                            sum=sum+durations[durations.length-1];
                        }
                    }
                    console.log(sum);
                    addrest(notes,sum,"bass");
                    // var beams = Beam.generateBeams(notes);
                    // beams.forEach((b) => {
                    //     b.setContext(context).draw();
                    // });
                    var voice = new Voice({ num_beats: frontbeat, beat_value: backbeat });
                    console.log(notes);
                    voice.addTickables(notes);
                    voices=voices.concat([voice]);
                }
                console.log("voices:",voices)
                // Format and justify the notes to 400 pixels.
                new Formatter().joinVoices(voices).format(voices, width);
                // Render voice
                voices.forEach(function (v) {
                    v.draw(context, lowstaves[i-highend]);
                });
            }
            for(var i=lowend;i<stavenum;i++){  //补齐休止
                var notesl=[
                    new StaveNote({keys: ["f/3"],duration:"wr",clef:"bass"})
                ]
                Formatter.FormatAndDraw(context, lowstaves[i], notesl);
            }
            function dotted(note) {
                Dot.buildAndAttach([note]);
                return note;
            }
            function caldur(str){   //生成音符时长
                var elements=str.split(',');
                var note;
                var dots;
                var res;
                var duration;
                if(elements[0]=="chord"){
                    dots=parseInt(elements[5]);
                    duration=durtoform(elements[4]);
                    console.log(duration);
                }
                else if(elements[0]=="rest"){
                    dots=0;
                    duration=durtoform(elements[2]);
                }
                else{
                    dots=parseInt(elements[3]);
                    duration=durtoform(elements[2]);
                }
                res=formtotime(duration);
                if(dots>=1)
                    res=2*formtotime(duration)-formtotime(duration)/(2**dots);
                return res;
            }
            function cnote(str,cleftype){   //创建音符
                var elements=str.split(',');
                console.log(elements);
                var note;
                var dots;
                if(elements[0]=="chord"){
                    var ks=elements[3].split(':');
                    var ms=elements[2].split(':');
                    dots=parseInt(elements[5]);
                    note=new StaveNote({ keys: ks, duration: durtoform(elements[4]) ,clef:cleftype,dots:dots});
                    for(var i=0;i<ms.length;i++){
                        if(ms[i]!='^'){
                            note.addModifier(new Accidental(ms[i]),i);
                        }
                    }
                    for(var i=0;i<dots;i++){
                        note=dotted(note);
                    }
                }
                else if(elements[0]=="rest"){
                    dots=0;
                    note=new StaveNote({ keys: [elements[1]], duration: durtoform(elements[2]) ,clef:cleftype,dots:dots});
                }
                else{
                    dots=parseInt(elements[3]);
                    note=new StaveNote({ keys: [elements[1]], duration: durtoform(elements[2]) ,clef:cleftype,dots:dots});
                    if(elements[0]!='^'){
                        note.addModifier(new Accidental(elements[0]));
                    }
                    for(var i=0;i<dots;i++){
                        note=dotted(note);
                    }
                }
                return note;
            }
            function durtoform(char){
                switch (char){
                    case '1':return 'w';
                    case 'h':return 'h';
                    case 'q':return 'q';
                    case 'e':return '8';
                    case 's':return '16';
                    case 't':return '32';
                }
            }
            function formtotime(form){
                var time;
                switch(form){
                    case 'w':time=1;break;
                    case 'h':time=1/2;break;
                    case 'q':time=1/4;break;
                    case '8':time=1/8;break;
                    case '16':time=1/16;break;
                    case '32':time=1/32;break;
                }
                return time;
            }
            function addrest(notes,duration,cleftype,durations){
                var rest=1-duration;
                while(rest<0){
                    rest+=durations[durations.length-1];
                    durations.splice(durations.length-1,1);
                    notes.splice(notes.length-1,1);
                }
                //console.log(rest);
                var e=6;
                if(cleftype=="treble"){
                    e=0;
                }
                var restnum=new Array();
                for(var i=0;i<6;i++){
                    restnum[i]=0;
                }
                if(rest>=1){
                    restnum[0]=1;
                    rest-=1;
                }
                if(rest>=0.5){
                    restnum[1]=1;
                    rest-=0.5;
                }
                if(rest>=0.25){
                    restnum[2]=1;
                    rest-=0.25;
                }
                if(rest>=0.125){
                    restnum[3]=1;
                    rest-=0.125;
                }
                if(rest>=1/16){
                    restnum[4]=1;
                    rest-=1/16;
                }
                if(rest>=1/32){
                    restnum[5]=1;
                    rest-=1/32;
                }
                console.log(restnum);
                for(var i=5;i>=0;i--){
                    if(restnum[i]==1){
                        notes[notes.length]=new StaveNote({ keys: [restlocat[i+e]], duration:Duration[i].concat('r'),clef:cleftype});
                        //console.log([restlocat[i+e]], Duration[i].concat('r'),cleftype)
                        //console.log(i,":",notes);
                    }
                }
            }
        }
        

    }   
</script>
<style>
    .item label{
    display: inline-block;
    width: 100px;
    height: 30px;
    text-align: center;
    color: #fff;
    line-height: 30px;
    background-color: skyblue;
    border-radius: 5px;
    cursor: pointer;
    }
    .item input{
        display: none;
    }

</style>